package com.elong.common.http;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.fluent.Form;
import org.apache.http.client.fluent.Request;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.message.BasicNameValuePair;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

/**
 * @ClassName: FluentHttpUtil
 * 
 * @Description: http访问的便利api
 * 
 * @author Guanguo.Gao
 * 
 * @date 2014年9月11日 下午4:04:38
 * 
 * @version V1.0
 */
public class FluentHttpUtil {
    
    //日志
    private static Log log = LogFactory.getLog(FluentHttpUtil.class);

    //统一的编码UTF-8
    public static final String DEFAULT_URL_ENCODE = "UTF-8";
    
    public static final String URL_PARAM_CONNECT = "&";
    
    private  HttpClient client;
    
    //client context
    private  HttpClientContext context = HttpClientContext.create();
    
    //cookie store
    private CookieStore cookieStore = new BasicCookieStore();
    
    private FluentHttpUtil(){
	this.client = HttpClientUtil.getInstance();
	context.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
    }
    
    private FluentHttpUtil(HttpClientConnectionManager connectionManager){
	this.client = HttpClientUtil.getInstance(connectionManager);
    }
    
    /**
     * getInstance
      * @Title: getInstance
      * @Description: 获取实例
      * @return
     */
    public static FluentHttpUtil getInstance(){
	return new FluentHttpUtil();
    }
    
    
    /**
     * getter method for client
     * @return the client
     */
    public HttpClient getClient() {
        return client;
    }

    /**
     * setter method for client
     * @Description the client to set
     * @param client 
     */
    public void setClient(HttpClient client) {
        this.client = client;
    }

    
    /**
     * @Description: urlget数据
     * @param url
     * @param params
     * @param isReserveCookie
     * @return
     * @throws
     */
    public HttpResult URLGet(String url, Map<String, String> params){
        return URLGet(url, params, false);
    }
    
    /**
     * @Description: urlget数据
     * @param url
     * @param params
     * @param isReserveCookie
     * @return
     * @throws
     */
    public  HttpResult URLGet(String url, Map<String, String> params, boolean isReserveCookie){
        return URLGet(url, params, DEFAULT_URL_ENCODE, isReserveCookie);
    }
    
    /**
     * @Description: get请求
     * @param url
     * @param params
     * @param enc
     * @return
     * @throws
     */
    public  HttpResult URLGet(String url, Map<String, String> params, String enc, boolean isReserveCookie){
        StringBuffer strtTotalURL = new StringBuffer("");
        strtTotalURL.append(url);
        if(params != null){
            if(strtTotalURL.indexOf("?") == -1) {
                strtTotalURL.append("?").append(getUrl(params, enc));  
            } else {
                strtTotalURL.append("&").append(getUrl(params, enc));  
            }  
        }
        log.debug("GET请求URL = \n" + strtTotalURL.toString());  
        
        HttpGet httpget = new HttpGet(strtTotalURL.toString());
        //setHttpHeader(httpget);
        HttpResponse response = null;
        HttpResult result = null;
        try {
            response = client.execute(httpget);
            result = HttpUtil.getContentFromResponse(response);
            Integer retCode = result.getRetCode();
            if(retCode == HttpStatus.SC_MOVED_TEMPORARILY
                    || retCode == HttpStatus.SC_MOVED_PERMANENTLY
                    || retCode == HttpStatus.SC_SEE_OTHER
                    || retCode == HttpStatus.SC_TEMPORARY_REDIRECT){
                String redirectUrl = HttpUtil.getRedirectUrl(httpget, response, context);
                httpget = new HttpGet(redirectUrl);
                response = client.execute(httpget, context);
                result = HttpUtil.getContentFromResponse(response);
            }
            if(isReserveCookie)
                result.setCookies(cookieStore.getCookies());
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            response = null;
            httpget.releaseConnection();
        }
        return result;
    }
    
    /**
     * @Description: url post 请求
     * @param url
     * @param params
     * @return
     * @throws
     */
    public HttpResult URLPost(String url, Map<String, String> params){
        return URLPost(url, params, false);
    }
    
    public  HttpResult URLPost(String url, Map<String, String> params, boolean isReserveCookie){
        return URLPost(url, params, DEFAULT_URL_ENCODE, isReserveCookie);
    }
    
    /**
     * @Description: get请求
     * @param url
     * @param params
     * @param enc
     * @return
     * @throws
     */
    public  HttpResult URLPost(String url, Map<String, String> params, String enc, boolean isReserveCookie){
        HttpResult result = null;
        List<NameValuePair> formparams = new ArrayList<NameValuePair>();
        for(String key : params.keySet()){
            formparams.add(new BasicNameValuePair(key, params.get(key)));
        }
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
        HttpPost httppost = new HttpPost(url);
        httppost.setEntity(entity);
        //setHttpHeader(httppost);
        HttpResponse response = null;
        try {
            response = client.execute(httppost);
            result = HttpUtil.getContentFromResponse(response);
            if(isReserveCookie)
                result.setCookies(cookieStore.getCookies());
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            response = null;
            httppost.releaseConnection();
        }
        return result;
    }
    
    /**
     * @Description: get请求
     * @param url
     * @param params
     * @param enc
     * @return
     * @throws
     */
    public  HttpResult postString(String url, String contentType, String encoding, String content){
        HttpResult result = null;
        StringEntity entity = new StringEntity(content, Consts.UTF_8);
        HttpPost httppost = new HttpPost(url);
        httppost.setEntity(entity);
        setHttpHeader(httppost);
        HttpResponse response = null;
        try {
            response = client.execute(httppost);
            result = HttpUtil.getContentFromResponse(response);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            response = null;
            httppost.releaseConnection();
        }
        return result;
    }
    
    
    
    /** 
     * 据Map生成URL字符串 
     * @param map 
     *          Map 
     * @param valueEnc 
     *          URL编码 
     * @return 
     *          URL 
     */  
    private static String getUrl(Map<String, String> map, String valueEnc) {  
          
        if (null == map || map.keySet().size() == 0) {  
            return "";  
        }  
        StringBuffer url = new StringBuffer();  
        Set<String> keys = map.keySet();  
        for (Iterator<String> it = keys.iterator(); it.hasNext();) {  
            String key = it.next();  
            if (map.containsKey(key)) {  
                String val = map.get(key);  
                String str = val != null ? val : "";  
                try {  
                    str = URLEncoder.encode(str, valueEnc);  
                } catch (UnsupportedEncodingException e) {  
                    e.printStackTrace();  
                }  
                url.append(key).append("=").append(str).append(URL_PARAM_CONNECT);  
            }  
        }  
        String strURL = "";  
        strURL = url.toString();  
        if (URL_PARAM_CONNECT.equals("" + strURL.charAt(strURL.length() - 1))) {  
            strURL = strURL.substring(0, strURL.length() - 1);  
        }  
          
        return (strURL);  
    }  
    
    /**
     * 
     * fluent api 发送get请求
     */
    public static String sendGet(String url) {
        String str = "";
        try {
            str = Request.Get(url).connectTimeout(1000)
                    .socketTimeout(1000).execute().returnContent().asString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }
    
    public static void setHttpHeader(HttpRequestBase base){
        //base.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.103 Safari/537.36");
        base.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
        base.addHeader("Accept-Language", "en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4");
        base.addHeader("Connection", "keep-alive");
    }

    public static void sendPost(String url, Map<String, String> paramMap, String enc) {
        try {
            List<NameValuePair> formparams = new ArrayList<NameValuePair>();
            for(String key : paramMap.keySet()){
                formparams.add(new BasicNameValuePair(key, paramMap.get(key).toString()));
            }
            UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
            Request.Post("http://train.elong.com").useExpectContinue()
                    .version(HttpVersion.HTTP_1_1)
                    .body(entity)
                    .bodyString("import stuff", ContentType.DEFAULT_TEXT)
                    .execute().returnContent().asBytes();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void sendPostViaProxy() {
        // Execute a POST with a custom header through the proxy containing a
        // request body
        // as an HTML form and save the result to the file
        try {
            Request.Post("http://somehost/some-form")
                    .addHeader("X-Custom-header", "stuff")
                    .viaProxy(new HttpHost("myproxy", 8080))
                    .bodyForm(
                            Form.form().add("username", "vip")
                                    .add("password", "secret").build())
                    .execute().saveContent(new File("result.dump"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void sendMultiRequest() {
        Executor executor = Executor.newInstance()
                .auth(new HttpHost("somehost"), "username", "password")
                .auth(new HttpHost("myproxy", 8080), "username", "password")
                .authPreemptive(new HttpHost("myproxy", 8080));
        try {
            executor.execute(Request.Get("http://somehost/")).returnContent()
                    .asString();
            executor.execute(
                    Request.Post("http://somehost/do-stuff")
                            .useExpectContinue()
                            .bodyString("Important stuff",
                                    ContentType.DEFAULT_TEXT)).returnContent()
                    .asString();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Document handleResponse() {
        Document result = null;
        try {
            result = Request.Get("http://www.elong.com").execute()
                    .handleResponse(new ResponseHandler<Document>() {
                        public Document handleResponse(final HttpResponse response)
                                throws IOException {
                            StatusLine statusLine = response.getStatusLine();
                            HttpEntity entity = response.getEntity();
                            if (statusLine.getStatusCode() >= 300) {
                                throw new HttpResponseException(statusLine
                                        .getStatusCode(), statusLine
                                        .getReasonPhrase());
                            }
                            if (entity == null) {
                                throw new ClientProtocolException(
                                        "Response contains no content");
                            }
                            DocumentBuilderFactory dbfac = DocumentBuilderFactory
                                    .newInstance();
                            try {
                                DocumentBuilder docBuilder = dbfac
                                        .newDocumentBuilder();
                                ContentType contentType = ContentType
                                        .getOrDefault(entity);
                                if (!contentType
                                        .equals(ContentType.TEXT_HTML)) {
                                    throw new ClientProtocolException(
                                            "Unexpected content type:"
                                                    + contentType);
                                }
                                String charset = contentType.getCharset().name();
                                if (charset == null) {
                                    charset = Charset.defaultCharset().name();
                                }
                                return docBuilder.parse(entity.getContent(),
                                        charset);
                            } catch (ParserConfigurationException ex) {
                                throw new IllegalStateException(ex);
                            } catch (SAXException ex) {
                                throw new ClientProtocolException(
                                        "Malformed XML document", ex);
                            }
                        }
                    });
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }
    
    public static void main(String[] args) {
        HttpResult result = FluentHttpUtil.getInstance().URLGet("http://www.elong.com", null);
        System.out.println(result.getRetContent());
    }
}
